package de.axone.wash;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.util.Set;

import org.junit.Test;

import de.axone.wash.Wash.NotFoundException;
import de.axone.wash.Wash.Type;
import de.axone.wash.Wash.WrongTypeException;
import de.axone.wash.WashTools.TypeValue;


public class Wash___Test {
	
	@Test
	public void testWashTools() throws Exception {
		
		// String coding
		System.out.println( "Test String encoding..." );
		
		String s = "Test\n1234";
		
		String encoded = WashTools.encodeString( s );
		String decoded = WashTools.decodeString( encoded );
		
		assertEquals( "Test\\n1234", encoded );
		assertEquals( s, decoded );
		
		// data coding
		System.out.println( "Test data encoding..." );
		
		byte[] data = new byte[ 256 ];
		for( int i = 0; i < 256; i++ ) {
			
			data[ i ] = (byte)i;
		}
		
		String encodedBin = WashTools.encodeData( data );
		byte[] decodedBin = WashTools.decodeData( encodedBin );
		
		for( int i = 0; i < 256; i++ ) {
			
			assertEquals( data[ i ], decodedBin[ i ] );
		}
		
		// Decode-Value
		TypeValue tv;
		tv = WashTools.decodeValue( "\"string" );
		assertEquals( Type.STRING, tv.type );
		assertEquals( String.class, tv.value.getClass() );
		assertEquals( "string", tv.value );
		
		tv = WashTools.decodeValue( "i123" );
		assertEquals( Type.INTEGER, tv.type );
		assertEquals( Long.class, tv.value.getClass() );
		assertEquals( (long)123, tv.value );
		
		tv = WashTools.decodeValue( "f123.45" );
		assertEquals( Type.FLOAT, tv.type );
		assertEquals( Double.class, tv.value.getClass() );
		assertEquals( 123.45, tv.value );
		
		tv = WashTools.decodeValue( "bTRUE" );
		assertEquals( Type.BOOLEAN, tv.type );
		assertEquals( Boolean.class, tv.value.getClass() );
		assertEquals( true, tv.value );
		
		tv = WashTools.decodeValue( "d123" );
		assertEquals( Type.DATA, tv.type );
		assertEquals( byte[].class, tv.value.getClass() );
	}
	
	@Test
	public void testWash() throws Exception {

		Wash wash = new DefaultWash();
		
		// Create
		System.out.println( "Test create..." );
		
		wash.addField( "field.ok.string", Wash.Type.STRING, "Teststring" );
		wash.addField( "field.ok.integer", Wash.Type.INTEGER, (long)10 );
		wash.addField( "field.ok.float", Wash.Type.FLOAT, (double)10 );
		wash.addField( "field.ok.boolean", Wash.Type.BOOLEAN, true );
		wash.addField( "field.ok.data", Wash.Type.DATA, new byte[]{ 0x00, 0x01, 0x02, 0x03 } );
		
		Set<String> fieldNames = wash.getFieldNames();
		
		assertTrue( fieldNames.size() == 5 );
		
		// Setter
		System.out.println( "Test setters..." );
		
		wash.setString( "field.ok.string", "test" );
		wash.setLong( "field.ok.integer", 10l );
		wash.setDouble( "field.ok.float", 10.0 );
		wash.setBoolean( "field.ok.boolean", false );
		wash.setData( "field.ok.data", new byte[]{ 0x10, 0x20, 0x30, 0x40, 0x50 } );
		
		// Fails:
		System.out.println( "Test setter fails..." );
		
		try { wash.setString( "field.not.here", "test" ); fail(); } catch( NotFoundException e ) {}
		
		try { wash.set( "field.ok.string", (long)10 ); fail(); } catch( WrongTypeException e ) {}
		try { wash.set( "field.ok.string", (double)10 ); fail(); } catch( WrongTypeException e ) {}
		try { wash.set( "field.ok.string", true ); fail(); } catch( WrongTypeException e ) {}
		try { wash.set( "field.ok.string", new byte[] { 0x7f } ); fail(); } catch( WrongTypeException e ) {}
		
		try { wash.set( "field.ok.integer", "blah" ); fail(); } catch( WrongTypeException e ) {}
		try { wash.set( "field.ok.integer", (double)10 ); fail(); } catch( WrongTypeException e ) {}
		try { wash.set( "field.ok.integer", true ); fail(); } catch( WrongTypeException e ) {}
		try { wash.set( "field.ok.integer", new byte[] { 0x7f } ); fail(); } catch( WrongTypeException e ) {}
		
		try { wash.set( "field.ok.float", (long)10 ); fail(); } catch( WrongTypeException e ) {}
		try { wash.set( "field.ok.float", "blah" ); fail(); } catch( WrongTypeException e ) {}
		try { wash.set( "field.ok.float", true ); fail(); } catch( WrongTypeException e ) {}
		try { wash.set( "field.ok.float", new byte[] { 0x7f } ); fail(); } catch( WrongTypeException e ) {}
		
		try { wash.set( "field.ok.boolean", "blah" ); fail(); } catch( WrongTypeException e ) {}
		try { wash.set( "field.ok.boolean", (long)10 ); fail(); } catch( WrongTypeException e ) {}
		try { wash.set( "field.ok.boolean", (double)10 ); fail(); } catch( WrongTypeException e ) {}
		try { wash.set( "field.ok.boolean", new byte[] { 0x7f } ); fail(); } catch( WrongTypeException e ) {}
		
		try { wash.set( "field.ok.data", "blah" ); fail(); } catch( WrongTypeException e ) {}
		try { wash.set( "field.ok.data", (long)10 ); fail(); } catch( WrongTypeException e ) {}
		try { wash.set( "field.ok.data", (double)10 ); fail(); } catch( WrongTypeException e ) {}
		try { wash.set( "field.ok.data", true ); fail(); } catch( WrongTypeException e ) {}
		
		
		// Getter
		System.out.println( "Test getter..." );
		
		assertEquals( String.class, wash.get( "field.ok.string" ).getClass() );
		assertEquals( "test", wash.get( "field.ok.string" ) );
		
		assertEquals( Long.class, wash.get( "field.ok.integer" ).getClass() );
		assertEquals( (long)10, wash.get( "field.ok.integer" ) );
		
		assertEquals( Double.class, wash.get( "field.ok.float" ).getClass() );
		assertEquals( (double)10, wash.get( "field.ok.float" ) );
		
		assertEquals( Boolean.class, wash.get( "field.ok.boolean" ).getClass() );
		assertEquals( false, wash.get( "field.ok.boolean" ) );
		
		assertEquals( byte[].class, wash.get( "field.ok.data" ).getClass() );
		assertEquals( 5, ((byte[])wash.get( "field.ok.data" )).length );
		assertEquals( (byte)0x10, ((byte[])wash.get( "field.ok.data" ))[ 0 ] );
		assertEquals( (byte)0x20, ((byte[])wash.get( "field.ok.data" ))[ 1 ] );
		assertEquals( (byte)0x30, ((byte[])wash.get( "field.ok.data" ))[ 2 ] );
		assertEquals( (byte)0x40, ((byte[])wash.get( "field.ok.data" ))[ 3 ] );
		assertEquals( (byte)0x50, ((byte[])wash.get( "field.ok.data" ))[ 4 ] );
		
		wash.setString( "field.ok.string", "1\n2\\3" );
		
		// Serialization
		System.out.println( "Test serialization..." );
		
		String s = wash.serialize();
		
		Wash wash2 = new DefaultWash();
		
		wash2.deserialize( s );
		
		/*
		for( String name : wash2.getFieldNames() ) {
			
			System.err.println( name );
		}
		*/
		
		assertEquals( wash.get( "field.ok.string" ), wash2.get( "field.ok.string" ) );
		assertEquals( wash.get( "field.ok.integer" ), wash2.get( "field.ok.integer" ) );
		assertEquals( wash.get( "field.ok.float" ), wash2.get( "field.ok.float" ) );
		assertEquals( wash.get( "field.ok.boolean" ), wash2.get( "field.ok.boolean" ) );
		assertEquals( ((byte[])wash.get( "field.ok.data" )).length,
				((byte[])wash2.get( "field.ok.data" )).length );
		
		
		String data = "" +
				"field1=\"short string\n" +
				"field2=\"long \n" +
				" str\n" +
				" ing\n" +
				"field3=i1234\n" +
				"field4=f123.45\n" +
				"field5=bTRUE";
		
		Wash wash3 = new DefaultWash();
		wash3.deserialize( data );
		
		assertEquals( String.class, wash3.get( "field1" ).getClass() );
		assertEquals( "short string", wash3.get( "field1" ) );
		
		assertEquals( String.class, wash3.get( "field2" ).getClass() );
		assertEquals( "long string", wash3.get( "field2" ) );
		
		assertEquals( Long.class, wash3.get( "field3" ).getClass() );
		assertEquals( (long)1234, wash3.get( "field3" ) );
		
		assertEquals( Double.class, wash3.get( "field4" ).getClass() );
		assertEquals( 123.45, wash3.get( "field4" ) );
		
		assertEquals( Boolean.class, wash3.get( "field5" ).getClass() );
		assertEquals( true, wash3.get( "field5" ) );
		
		// NULL-Handling
		wash.set( "field.ok.string", null );
		wash.set( "field.ok.integer", null );
		wash.set( "field.ok.float", null );
		wash.set( "field.ok.boolean", null );
		wash.set( "field.ok.data", null );
		
		assertEquals( Type.STRING, wash.getType( "field.ok.string" ) );
		assertEquals( Type.INTEGER, wash.getType( "field.ok.integer" ) );
		assertEquals( Type.FLOAT, wash.getType( "field.ok.float" ) );
		assertEquals( Type.BOOLEAN, wash.getType( "field.ok.boolean" ) );
		assertEquals( Type.DATA, wash.getType( "field.ok.data" ) );
		
		String nullOut = wash.serialize();
		
		String nullData = "" +
			"field.ok.string=NULL\n" +
			"field.ok.integer=NULL\n" +
			"field.ok.float=NULL\n" +
			"field.ok.boolean=NULL\n" + 
			"field.ok.data=NULL\n";
		
		assertEquals( nullData, nullOut );
		
		Wash nullWash = new DefaultWash();
		nullWash.deserialize( nullData );
		
		assertEquals( null, nullWash.get( "field.ok.string" ) );
		assertEquals( null, nullWash.get( "field.ok.integer" ) );
		assertEquals( null, nullWash.get( "field.ok.float" ) );
		assertEquals( null, nullWash.get( "field.ok.boolean" ) );
		assertEquals( null, nullWash.get( "field.ok.data" ) );
		
		assertEquals( null, nullWash.getString( "field.ok.string" ) );
		assertEquals( null, nullWash.getLong( "field.ok.integer" ) );
		assertEquals( null, nullWash.getDouble( "field.ok.float" ) );
		assertEquals( null, nullWash.getBoolean( "field.ok.boolean" ) );
		assertEquals( null, nullWash.getData( "field.ok.data" ) );
		
		assertEquals( null, nullWash.getType( "field.ok.string" ) );
		assertEquals( null, nullWash.getType( "field.ok.integer" ) );
		assertEquals( null, nullWash.getType( "field.ok.float" ) );
		assertEquals( null, nullWash.getType( "field.ok.boolean" ) );
		assertEquals( null, nullWash.getType( "field.ok.data" ) );
		
	}
}
